using System;
using System.Collections.Generic;
using mp;

namespace mp.Models
{
	public class SymbolTable
	{
		public SymbolTable()
		{
			Implementation = new Dictionary<string, Symbol>();
			prevTable = null;
			Level = 0;
		}
		
		public void AddSymbol(Symbol newSymbol)
		{
			if(!ContainsSymbol(newSymbol.Lexeme))
			{
				if(this.Level == 0)
					newSymbol.Offset = GetSize() + 1 + AdditionalOffset;
				else
					newSymbol.Offset = GetSize() + AdditionalOffset;
				newSymbol.Level = this.Level;
				Implementation.Add(newSymbol.Lexeme, newSymbol);
			}
			else
				throw new Exception("The lexeme " + newSymbol.Lexeme + " has already been declared.");
		}
		
		public void AddSymbol(int kind, string lexeme, int type)
		{
			if(!ContainsSymbol(lexeme))
			{
				Symbol newSymbol = new Symbol(kind, lexeme, type);
				if(this.Level == 0)
					newSymbol.Offset = GetSize() + 1 + AdditionalOffset;
				else
					newSymbol.Offset = GetSize() + AdditionalOffset;
				newSymbol.Level = this.Level;
				Implementation.Add(lexeme, newSymbol);
			}
			else
				throw new Exception("The lexeme " + lexeme + " has already been declared.");
		}
		
		public bool ContainsSymbol(string lexeme)
		{
			return Implementation.ContainsKey(lexeme);
		}
		
		public int GetSize()
		{
			return this.Implementation.Count;
		}
		
		public int GetVariableCount()
		{
			int count = 0;
			foreach(var value in Implementation.Values)
			{
				if(value.Kind == SymbolKind.Variable.GetHashCode())
					count++;
			}
			return count;
		}
		
		public Symbol GetSymbol(string lexeme)
		{
			SymbolTable tempTable = this;
			
			while(tempTable != null)
			{
				if(tempTable.ContainsSymbol(lexeme))
					return tempTable.Implementation[lexeme];
				tempTable = tempTable.prevTable;
			}
			return null;
		}
		
		public int GetSymbolType(string lexeme)
		{
			return GetSymbol(lexeme).Type;
		}
		
		public int GetSymbolKind(string lexeme)
		{
			return GetSymbol(lexeme).Kind;
		}
		
		public Dictionary<string, Symbol> Implementation;
		public int Level;
		public string Name;
		public SymbolTable prevTable;
		public int AdditionalOffset = 0;
	}
	
	public class Symbol
	{
		public Symbol(int kind, string lexeme, int type)
		{
			this.Kind = kind;
			this.Lexeme = lexeme;
			this.Type = type;
		}
		
		public Symbol()
		{
		}
		
		public int Offset;
		public int Kind;
		public string Lexeme;
		public int ParameterMode;
		public List<Parameter> Params = new List<Parameter>();
		public int Type;
		public string Label;
		public int Level;
	}
	
	public class Parameter
	{
		public Parameter(int type, int mode)
		{
			this.Type = type;
			this.Mode = mode;
		}
		
		public int Type;
		public int Mode;
	}
}

